<?php
/**
 ***********************************************************************************************************************
 * 缓存基类
 ***********************************************************************************************************************
 */
namespace artisan;

/**
 * Class IDriver
 * @package artisan
 */
interface IDriver {
    /**
     * 约束实现者必须实现该方法
     */
    static function connect($alias = '');
}

/**
 * Class cache
 * @package artisan
 */
abstract class CacheBase implements IDriver
{
    /**
     * @var array
     */
    protected static $config = [];

    /**
     * @var array
     */
    protected static $instances = [];

    /**是否是某种类型的驱动
     * @param $driverKey:如predis
     * @return bool
     */
    public static function isThisKindOfDriver($driverKey)
    {
        return stristr(get_called_class(),$driverKey)===false?false:true;
    }

    /**
     * @param $key
     * @param $val
     * @param int $expire
     * @return bool
     */
    public static function set($key, $val, $ttl = 86400)
    {
        return static::connect()->set($key, $val, $ttl);
    }

    /**
     * @param $key
     * @return bool|mixed|null|string
     */
    public static function get($key)
    {
        return static::connect()->get($key);
    }

    /**
     * @param array $keys
     * @return array
     */
    public static function mget(array $keys)
    {
        return static::connect()->mget($keys);
    }

    /**
     * @param array $keyVals
     * @return bool
     */
    public static function mset(array $keyVals)
    {
        return static::connect()->mset($keyVals);
    }

    /**
     * @param $key
     * @param $val
     * @return string
     */
    public static function getset($key, $val)
    {
        return static::connect()->getset($key, $val);
    }

    /**
     * @param $key
     * @param $ttl
     * @return bool
     */
    public static function expire($key, $ttl)
    {
        return static::connect()->expire($key, $ttl);
    }

    /**
     * @param $key
     * @param $timestamp
     * @return bool
     */
    public static function expireat($key, $timestamp)
    {
        return static::connect()->expireat($key, $timestamp);
    }

    /**
     * @param $key
     * @return int
     */
    public static function type($key)
    {
        return static::connect()->type($key);
    }

    /**
     * @param $key
     * @return int
     */
    public static function del($key)
    {
        return static::connect()->del($key);
    }

    /**
     * @param $key
     * @return bool
     */
    public static function exists($key)
    {
        return static::connect()->exists($key);
    }

    /**
     * @param $key
     * @param $val
     * @return int
     */
    public static function append($key, $val)
    {
        return static::connect()->append($key, $val);
    }

    /**
     * @param $key
     * @return int
     */
    public static function incr($key)
    {
        return static::connect()->incr($key);
    }

    /**
     * @param $key
     * @param $val
     * @return int
     */
    public static function incrBy($key, $val)
    {
        return static::connect()->incrBy($key, $val);
    }

    /**
     * @param $key
     * @return int
     */
    public static function decr($key)
    {
        return static::connect()->decr($key);
    }

    /**
     * @param $key
     * @param $val
     * @return int
     */
    public static function decrBy($key, $val)
    {
        return static::connect()->decrBy($key, $val);
    }

    /**
     * hMset( $key, $hashKeys )
     * @param $key
     * @param $hashKey
     * @param string $value
     * @return int
     */
    public static function hset($key, $hashKey, $value = '')
    {
        return static::connect()->hSet($key, $hashKey, $value);
    }

    /**
     * @param $key
     * @param $hashKey
     * @return int|string
     */
    public static function hget($key, $hashKey)
    {
        return static::connect()->hGet($key, $hashKey);
    }

    /**
     * @param $key
     * @param $hashKeys
     * @return bool|int
     */
    public static function hMset($key, $hashKeys)
    {
        return static::connect()->hMset($key, $hashKeys);
    }

    /**
     * @param $key
     * @return array
     */
    public static function hGetAll($key)
    {
        return static::connect()->hGetAll($key);
    }

    /**
     * @return bool|string
     */
    public static function ping()
    {
        return static::connect()->ping();
    }

    /**
     * 其他Redis方法实现
     * @param $method
     * @param $args
     * @return mixed
     */
    public static function __callStatic($method, $args)
    {
        return call_user_func_array(array(static::connect(), $method), $args);
    }

    /**
     * 按权重选择服务器
     * @param string $alias
     * @return array
     */
    public static function getServer($alias)
    {
        // 检测配置文件
        if(!(static::$config) && !(static::$config = config::get('cache'))) {
            exit('Error cache config give!');
        }

        // 兼容旧版配置
        $config = !empty(static::$config[$alias]) ? static::$config[$alias] : static::$config;

        // 按权重选择服务器
        $count = 0;
        $servers = array();
        foreach($config  as $server) {
            if(!isset($server['enable']) || $server['enable'] == true) {
                $server['weight'] = empty($server['weight']) ? 100 : $server['weight'];
                $servers[] = $server;
                $count++;
            }
        }

        $weight_sum = 0;
        foreach($servers as $val) {
            $weight_sum += $val['weight'];
        }

        $roll = rand(1, $weight_sum);
        $tmp = 0;
        $key = 0;
        foreach($servers as $server_key => $server) {
            $min = $tmp;
            $tmp += $server['weight'];
            $max = $tmp;
            if ($roll > $min && $roll <= $max) {
                $key = $server_key;
                break;
            }
        }

        return [
            'host' => $servers[$key]['host'],
            'port' => $servers[$key]['port'],
            'timeout' => empty($servers[$key]['timeout']) ? '' : $servers[$key]['timeout'],
            'is_cluster' => isset($servers[$key]['is_cluster']),
        ];
    }

    /**
     * @return \Redis
     */
    public static function getInstance()
    {
        return static::connect()->getInstance();
    }
}